// JavaScript Document

function SceneNode(parentNode, displayObject, isAabbTest)
{
    if (this instanceof SceneNode)
    {
        this.parent = null;
        if (parentNode)
        {
            parentNode.addChild(this);
        }
        this.children = new Array();
        this.displayObject = null;
        this.isAabbTest = true;
        if (displayObject !== undefined)
        {
            this.displayObject = displayObject;
        }
        if (isAabbTest !== undefined)
        {
            this.isAabbTest = isAabbTest;
        }
        this._isEnableHitTest = true;
        this._isEnable = true;
        
        this.aabb = new Rect();
        this.localAabb = new Rect();
        this.finalLocalAabb = new Rect();
        
        this.eventMouseOver = null;
        this.eventMouseDown = null;
        this.eventMouseUp = null;
        this.eventMouseLeave = null;
        
        // only support translate now.
        // rotate and scale add when needed.
        // dirty update optimise add when needed too.
        // current just update all.
        this.position = new Point();
        this.finalPosition = new Point();
        this.track = null;
    }
    else
    {
        return new SceneNode(parentNode, displayObject, isAabbTest);
    }
}

SceneNode.prototype.onMouseOver = function()
{
    if (this.eventMouseOver)
    {
        this.eventMouseOver();
    }
}

SceneNode.prototype.onMouseDown = function()
{
    if (this.eventMouseDown)
    {
        this.eventMouseDown();
    }
}

SceneNode.prototype.onMouseUp = function()
{
    if (this.eventMouseUp)
    {
        this.eventMouseUp();
    }
}

SceneNode.prototype.onMouseLeave = function()
{
    if (this.eventMouseLeave)
    {
        this.eventMouseLeave();
    }
}

SceneNode.prototype.onMouseClick = function()
{
	if (this.eventMouseClick)
	{
		this.eventMouseClick();
	}
}

SceneNode.prototype.setTrack = function(track)
{
	this.track = track;
}

SceneNode.prototype.detach = function()
{
	if (this.parent)
	{
		this.parent.removeChild(this);
	}
}

SceneNode.prototype.enableHitTest = function(enable)
{
    this._isEnableHitTest = enable;
}

SceneNode.prototype.isEnableHitTest = function()
{
    return this._isEnableHitTest;
}

SceneNode.prototype.isEnable = function()
{
    return this._isEnable;
}

SceneNode.prototype.enable = function(enable)
{
    this._isEnable = enable;
}

SceneNode.prototype.update = function(force)
{
    if (!this.isEnable()) return;
    this.finalPosition.setValue(this.position);
	if (this.track)
	{
		this.finalPosition.add(this.track.getCurOffset());
	}
    if (this.parent)
    {
        this.finalPosition.add(this.parent.finalPosition);
    }
    var numChildren = this.children.length;
    for (var i = 0; i < numChildren; i++)
    {
       this.children[i].update(force);
    }
    
    this._updateAabb(force);
}

SceneNode.prototype._updateAabb = function(force)
{
    if (!this.isEnable()) return;
    this.finalLocalAabb = Rect.s_addPoint(this.localAabb, this.finalPosition);
    this.aabb.setValue(this.finalLocalAabb);
    
    var numChildren = this.children.length;
    for (var i = 0; i < numChildren; i++)
    {
        if (this.children[i].isEnable())
            this.aabb.addRect(this.children[i].aabb);
    }
}

SceneNode.prototype.movePosition = function(offset)
{
	this.position.add(offset);
}

SceneNode.prototype.setPosition = function(pos)
{
    this.position.setValue(pos);
}

SceneNode.prototype.getPosition = function()
{
	return this.position;
}

SceneNode.prototype.setLocalAabb = function(aabb)
{
    this.localAabb.setValue(aabb);
}

SceneNode.prototype.addChild = function(child)
{
    assert(child.parent == null, "SceneNode::addChild, child should not has parent!");
    this.children.push(child);
    child.parent = this;
}

SceneNode.prototype.removeChild = function(node)
{
    node.parent = null;
    this.children.remove(node);
}

SceneNode.prototype.removeAllChildren = function(derived)
{
	var numChildren = this.children.length;
	for (var i = 0; i < numChildren; i++)
	{
		if (derived)
		{
			this.children[i].removeAllChildren(derived);
		}
		this.children[i].parent = null;
	}
	this.children = null;
}

SceneNode.prototype.hitTest = function(pt)
{
    if (!this.isEnableHitTest() || !this.isEnable())
        return null;
    if (!this.aabb.hitTest(pt))
        return null;
    
    var numChildren = this.children.length;
    for (var i = numChildren - 1; i >= 0; i--)
    {
        var _res = this.children[i].hitTest(pt);
        if (_res) return _res;
    }
    if (this.displayObject)
    {
        if (this.displayObject.isSupportHitTest && this.displayObject.isSupportHitTest() && this.displayObject.hitTest)
        {
            var localPt = Point.s_sub(pt, this.finalPosition);
			if (this.displayObject.hitTest(localPt))
				return this;
			else
				return null;
        }
        else
        {
            if (this.finalLocalAabb.hitTest(pt))
                return this;
            else
                return null;
        }
    }
}

SceneNode.prototype.updateDisplayObject = function(obj)
{
    this.displayObject = obj;
}

SceneNode.prototype.filterByAabb = function(aabb, renderList)
{
    if (!this.isEnable()) return;
	if (this.displayObject)
	{
		renderList.push(new RenderItem(this.finalPosition, this.displayObject));
	}
	// current ignore render aabb testing.
/*
    if (!this.aabb.isIntersect(aabb)) return;
    if (this.displayObject)
    {
        if (this.displayObject.isSupportTestAabb && this.displayObject.isSupportTestAabb() && this.displayObject.TestAabb)
        {
			var localAabb = new Rect();
			localAabb.setValue(aabb);
			localAabb.subPoint(this.finalPosition);
            if (this.displayObject.TestAabb(localAabb))
            {
                renderList.push(new RenderItem(this.finalPosition, this.displayObject));
            }
        }
        else
        {
            if (this.finalLocalAabb.isIntersect(aabb))
            {
                renderList.push(new RenderItem(this.finalPosition, this.displayObject));
            }
        }
    }
*/
    var numChildren = this.children.length;
    for (var i = 0; i < numChildren; i++)
    {
        this.children[i].filterByAabb(aabb, renderList);
    }
}

SceneNode.prototype.draw = function(ctx)
{
    if (!this.isEnable()) return;
    this.drawSelf(ctx);
    var numChildren = this.children.length;
    for (var i = 0; i < numChildren; i++)
    {
        this.children[i].draw(ctx);
    }
}

SceneNode.prototype.drawSelf = function(ctx)
{
    ctx.strokeStyle = 'rgba(0, 0, 255, 1)';
    ctx.strokeRect(this.aabb.x, this.aabb.y, this.aabb.dx, this.aabb.dy);
    if (this.displayObject)
    {
        this.displayObject.draw(ctx, this.finalPosition.x, this.finalPosition.y);
    }
}

// inherit from SceneNode
function Camera(parentNode, viewport_size)
{
    if (this instanceof Camera)
    {
        SceneNode.call(this, parentNode);
        this.viewport_size = new Point();
        this.viewport_size.setValue(viewport_size);
    }
    else
    {
        return new Camera(parentNode, viewport_size);
    }
}

inheritPrototype(Camera, SceneNode);

Camera.prototype.filterScene = function(node, renderList)
{
    var cameraAabb = new Rect(this.finalPosition.x, this.finalPosition.y, this.viewport_size.x, this.viewport_size.y);
    node.filterByAabb(cameraAabb, renderList);
}

Camera.prototype.translatePos = function(pt)
{
    return Point.s_add(this.finalPosition, pt);
}